思來想去,還是決定先將我理解的 this 用兩句話做個結論,再由此延伸後續的詳細差別。
this (例如: bind 、 call 、 apply 方法)。this如前面所提,決定this的方式取決於函式如何被呼叫。
所以再來要對於函式呼叫時 this 的綁定依序了解。
this來看看這個測試例子:
console.log(this);
如果我們直接調用 this 並查看,會發現 this 指向全域

所以當全域中有變數時:
var name = "Bill";
console.log(this.name);
因為 this 指向全域, 所以才可以取得同樣位於全域的 name 變數的值 Bill。

當我們 直接呼叫函式 的時候,此時的 this 指向全域
來看看測試例子驗證一下:
var name = "Bill";
function getName(){
console.log(this.name);
}
getName();

因此可以得到全域的 name 的值 Bill
thisMDN: 嚴格模式下,如果
this沒有定義到執行環境( Execution Context)內,其預設值就會是undefined。
寫些測試例子驗證一下:
'use strict'
function test(){
console.log(this);
}
test();
前面有提到,當我們直接呼叫函式的時候, this 會是指向全域而非 test 函式本身,所以在嚴格模式底下會因為 this 不是定義在執行環境(test 函式)中,所以會是 undefined。

如果是 呼叫物件的方法,此時的 this 指向該物件
var habbit = "Read books";
var obj = {
habbit: "Read comics",
getHabbit : function() {
return this.habbit;
}
}
console.log(obj.getHabbit());
此時的 this 指向 obj 物件。 所以 getHabbit 方法中的 this.habbit 會取得 obj 物件的 habbit 的值 Read comics ,而不是全域 habbit 變數的值 Read books。

以下注意!!!!
讓我們改寫一下測試的例子:
var habbit = "Read books";
var obj = {
habbit: "Read comics",
getHabbit : function() {
console.log('getHabbit方法的habbit值: ' + this.habbit);
function getAnotherHabbit(){
console.log('getAnotherHabbit方法的habbit值: ' + this.habbit);
}
getAnotherHabbit();
}
}
obj.getHabbit();
這時候依序會獲得什麼值呢?
解析的流程如下:
obj 物件的 getHabbit 方法,此時的 this 指向 obj 物件,所以可以看到 getHabbit 方法的值為 Read comics
getHabbit 的程式內容時,會執行 getAnotherHabbit 這個函式,由於在 getHabbit 方法是直接呼叫 getAnotherHabbit ,所以此時的 this 已變成指向全域(就是Window物件)。getAnotherHabbit 函式的 this 指向全域,所以就取得位於全域的變數值 Read books。
that 、 self 怎麼用?this 很常在執行過程中因為呼叫函式的方式改變,而 this 也跟著改變。
所以必須透過一個變數用來承接原本 this 的值 ,而常見的寫法就是that 或者 self 。
將前一個測試例子改寫一下:
var habbit = "Read books";
var obj = {
habbit: "Read comics",
getHabbit : function() {
var that = this;
console.log('getHabbit方法的habbit值: ' + this.habbit);
function getAnotherHabbit(){
console.log('getAnotherHabbit方法的habbit值: ' + that.habbit);
}
return getAnotherHabbit();
}
}
obj.getHabbit();
從圖中可以得知: 因為 that 的值為 this 指向 obj 物件時候的值,所以 that.habbit 自然也就會拿到 obj 物件中的 habbit 的值 Read comics。
而 that 則取得 obj 這個物件的相關資訊。

this事件中的 this 會指向那個綁定事件的元素
寫個例子來驗證:
<ul class="orderList">
<li>列表A</li>
<li>列表B</li>
<li>列表C</li>
<li>列表D</li>
<li>列表E</li>
</ul>
var orderList = document.querySelector('.orderList');
orderList.addEventListener('click',getListText);
function getListText(e){
console.log(this);
}
這裡在 <ul></ul> 上 綁定 click 事件,所以當點擊 <ul></ul> ,會得到 this 值為 <ul></ul> 這個 html 標籤的所有元素。

this關於立即函式(IIFE),在MDN中這麼解釋:
MDN: IIFE (Immediately Invoked Function Expression) 是一個定義完馬上就執行的 JavaScript function。
且立即函式的 this會指向全域
來個測試例子:
(function(){
console.log(this);
})();

call、apply 及 bind 指定 this 的值當我們如果需要一個特定的 this 值的時候,這時也許就會用到 call、apply 及 bind 強迫綁定 this。
關於 call、apply 及 bind 就讓我們依序往下看吧
關於 Function.prototype.call 在MDN有這麼一段解釋:
MDN: Function.prototype.call
使用給定的this參數以及分別給定的參數來呼叫某個函數
fun.call(thisArg[, arg1[, arg2[, ...]]])
簡單來說, 自己定義 this 的值並傳給目標函式當作該函式的 this 值
而MDN定義中提到的其他分別給定的參數則是 如果有設定除了 this 以外的參數,會將那些參數一併傳入目標函式中,如果不需要則不用設定。
還是很文謅謅,所以趕緊來寫個測試例子驗證看看:
var obj = {
name: "Bill",
habbit: "Read Books"
}
function introduce(){
console.log(this);
console.log(this.name + '\'s habbit is ' + this.habbit);
}
introduce.call(obj);
前面有提到,當 直接呼叫函式的時候, this 指向全域
但是當我們透過 call() 方法 將 obj 物件當作 this 傳入 introduce 函式的時候,此時已經綁定 this ,所以 introduce 函式才得以使用 obj 物件中的值。

來看看有額外設定其他參數時的情況:
function add(a,b){
console.log(this);
console.log('總和為: ' + (a+b));
}
add.call(null,2,3);

從結果可以看到, this 會是指向全域,而數值2及數值3被傳入 add 函式中,所以可以獲得加總後的值為 5。
關於 Function.prototype.apply 在MDN有這麼一段解釋:
MDN: Function.prototype.apply
apply() 方法會呼叫一個以this的代表值和一個陣列形式的值組(或是一個 array-like object )為參數的函式。
fun.apply(thisArg, [argsArray])
和 call() 差別在於 call()接受一連串的參數傳入,而 apply() 只接受陣列型式的參數
var obj = {
name: "Bill",
habbit: "Read Books"
}
function introduce(){
console.log(this);
console.log(this.name + '\'s habbit is ' + this.habbit);
}
introduce.apply(obj);
可以這邊看起來和 call() 執行結果沒有差別

所以我們再往下看看有額外的參數需要被輸入時的情形:
function add(numberAry){
console.log(numberAry);
const sumTotal = numberAry.reduce((acc,number)=> acc + number,0)
console.log('總和為: ' + sumTotal);
}
add.call(null,[2,3]);
因為接受陣列型別的參數,所以可以透過陣列的方法操作元素中的值,獲得總和值為 5。

最後一個是 bind() 方法, 在MDN有這麼一段解釋:
MDN: Function.prototype.bind()
bind() 方法,會建立一個新函式。該函式被呼叫時,會將 this 關鍵字設為給定的參數,並在呼叫時,帶有提供之前,給定順序的參數。
fun.bind(thisArg[, arg1[, arg2[, ...]]])
和前兩者差別為 使用 bind() 會回傳一個綁定自定義 this 值的函式提供我們呼叫。
var obj = {
name: "Bill",
habbit: "Read Books"
}
function introduce(){
console.log(this);
console.log(this.name + '\'s habbit is ' + this.habbit);
}
const newIntroduce = introduce.bind(obj);
console.log(newIntroduce);
newIntroduce();
從圖中可以看到透過變數 newIntroduce 儲存了 已經綁定 this 後的 introduce函式,而這裡的 this 指向的是 obj 物件。
所以當我們呼叫 newIntroduce 函式時,就可以取得與 call() 、 apply() 一樣的結果。

this建構式的 this 指向使用 new 關鍵字所建立的物件
function Person(name,age,habbit){
this.name = name;
this.age = age;
this.habbit = habbit;
console.log(this);
}
const person1 = new Person('Bill',22,'Read');

thisES6之後對於 this 有了額外的定義:
this箭頭函式並不擁有自己的 this 變數;使用的 this 值來自封閉的文本上下文,也就是說,箭頭函式遵循常規變量查找規則。因此,如果在當前範圍中搜索不到 this 變量時,他們最終會尋找其封閉範圍。
箭頭函式的this
簡單來說,大致可以歸納出幾個重點:
this
this 綁定會透過 範圍鏈(scope chain) 的觀念找到其作用域的this指向,並當作自己的this。在往下講之前,需要先稍微了解一下什麼是範圍鏈(scope chain)。
範圍鏈(scope chain) 的概念會於閉包(closure)的篇幅來詳細的理解。
這邊只要知道 當變數在自己的執行環境中如果找不到該變數,就會往外層尋找,直到找到後才停止
來看個測試例子:
const name = "Bill";
function getName(){
console.log(name);
}
getName();
當執行 getName 函式時,因為在函式的執行環境中沒有 name 這個變數,所以會依照範圍鏈(scope chain)的概念往外層尋找,所以會找到位於全域的 name 變數的值 Bill
了解箭頭函式(arrow function)的 this 與範圍鏈(scope chain)的觀念後,再來要看看測試的例子
var name = "Jack";
var obj = {
name: "Bill",
checkThis: () => { console.log(this.name);}
}
obj.checkThis();
箭頭函式的 this 會透過範圍鏈(scope chain)的觀念往外層尋找看看作用域的 this 是指向誰,會發現 this 指向 全域(因為obj物件位於全域),所以箭頭函式的 this 指向 全域,於是就會得到 name 的值 Jack
讓我們再看看另一個例子
var name = "Jack";
var obj = {
name: "Bill",
checkThis: function(){
test = () => {console.log(this.name);}
test();
}
}
obj.checkThis();
箭頭函式的 this 會透過範圍鏈(scope chain)的觀念往外層尋找看看作用域的 this 是指向誰,會發現 this 指向obj 物件(因為箭頭函式 test 的外層 checkThis 函式指向 obj物件),於是就會得到 name 的值 Bill。
關於 this 的學習就到這裡囉~
明天見~